package com.demo.personalaccountingbackend.filter;

import com.demo.personalaccountingbackend.data.MyUserDetails;
import com.demo.personalaccountingbackend.data.Result;
import com.demo.personalaccountingbackend.service.TokenService;
import com.demo.personalaccountingbackend.service.impl.MyUserDetailsService;
import com.demo.personalaccountingbackend.utils.JwtUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * 类描述信息
 *
 * @author zgx
 * @version 1.0
 * @date 2025/10/23 17:10
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final TokenService tokenService;

    private final JwtUtil jwtTokenUtil;

    private final MyUserDetailsService userDetailsService;
    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {

        String requestURI = request.getRequestURI();
        String method = request.getMethod();
        log.info("🔐 JWT过滤器处理: {} {}", method, requestURI);

        // 1. 放行不需要认证的路径
        if (shouldSkipAuthentication(requestURI)) {
            log.info("✅ 放行路径: {}", requestURI);
            filterChain.doFilter(request, response);
            return;
        }

        // 2. 获取并验证Token
        String token = getTokenFromRequest(request);
        if (token == null) {
            log.warn("❌ 缺少Token: {}", requestURI);
            sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "缺少访问令牌");
            return;
        }

        // 3. 验证Token有效性
        if (!tokenService.validateToken(token)) {
            log.warn("❌ Token无效: {}", requestURI);
            sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "访问令牌无效或已过期");
            return;
        }

        try {
            // 4. 设置认证信息
            String username = jwtTokenUtil.getUsernameFromToken(token);
            Long userId = jwtTokenUtil.getUserIdFromToken(token);
            log.info("✅ 用户认证成功: userId={}, username={}, path={}", userId, username, requestURI);
            // 关键：加载UserDetails对象，而不是使用字符串作为principal
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);

            // 创建Authentication对象
            UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new HashMap<String, Object>() {{
                put("userId", userId);
                put("username", username);
            }});

            // 设置details（可选）
            Map<String, Object> details = new HashMap<>();
            if (userDetails instanceof MyUserDetails) {
                details.put("userId", ((MyUserDetails) userDetails).getId());
            }
            details.put("username", username);
            authentication.setDetails(details);

            SecurityContextHolder.getContext().setAuthentication(authentication);

            // 5. 将用户信息设置到请求属性中（兼容原有代码）
            request.setAttribute("userId", userId);
            request.setAttribute("username", username);

        } catch (Exception e) {
            log.error("❌ Token解析异常: {}", e.getMessage());
            sendErrorResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "令牌解析失败");
            return;
        }

        // 6. 继续过滤器链
        filterChain.doFilter(request, response);
    }

    /**
     * 判断是否跳过认证
     */
    private boolean shouldSkipAuthentication(String requestURI) {
        // 放行路径列表
        String[] skipPaths = {
                "/auth/login",
                "/auth/register",
                "/auth/refresh",
                "/auth/logout",
                "/auth/health",
                "/swagger-ui",
                "/v3/api-docs",
                "/webjars",
                "/favicon.ico",
                //TODO 开发模式下，允许所有接口访问
                "/api"
        };

        // 放行OPTIONS请求（预检请求）
        if (requestURI.contains("options")) {
            return true;
        }

        for (String path : skipPaths) {
            if (requestURI.contains(path)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 从请求中获取Token
     */
    private String getTokenFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }

        // 也支持从参数中获取（可选）
        String tokenParam = request.getParameter("token");
        if (StringUtils.hasText(tokenParam)) {
            return tokenParam;
        }

        return null;
    }

    /**
     * 发送错误响应
     */
    private void sendErrorResponse(HttpServletResponse response, int status, String message) throws IOException {
        response.setStatus(status);
        response.setContentType("application/json;charset=UTF-8");

        Result<?> result = Result.failed(message);
        String jsonResponse = new ObjectMapper().writeValueAsString(result);

        PrintWriter writer = response.getWriter();
        writer.write(jsonResponse);
        writer.flush();
    }
}
